﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using Slam.Configuration;
using Slam.Logging;
using Slam.Data;
using Slam.SharePoint;
using System.Data.SqlClient;
using SlamCms.SharePoint.SlamProfiles.ContentItem.Data;
using SlamCms.SharePoint.SlamProfiles.ContentItem.Services;
using SlamCms.SharePoint.Tagging.FieldTypes;
using System.Collections;
using System.Text.RegularExpressions;
using SlamCms.SharePoint.Tagging.Services;
//using SlamCms.SharePoint.SmartFields;
using SlamCms.SharePoint.FieldTypes;
using Slam.FieldTypes;
using SlamCms.SharePoint.Core;

namespace SlamCms.SharePoint.SlamProfiles.ContentItem
{
	internal static class ContentItemEvents
	{
		internal static bool Create(SPListItem listItem, IDataServices dataServices, IConfigurationManager configuration, ILogger logger, DataService contentItemDataService, string dataSchema, string eventPrefix)
		{
			bool createReturn = false;
			string contentTypeName = "";
			try
			{
				contentTypeName = listItem.ContentType.Name.ToLower();
			}
			catch
			{
				//If this fails then it's definitely not a Wiki Page
			}
			if (contentTypeName.Contains("wiki"))
			{
				createReturn = true;
			}
			else
			{
				SPWeb site = listItem.Web;
				SPList parentList = listItem.ParentList;
				
				try
				{
					string typeName = SlamItemContext.TypeName;
					string logItemInformation = typeName + Environment.NewLine + " List: " + parentList.Title + Environment.NewLine + "ListItem: " + listItem.GetTitle() + ", " + listItem.GetUniqueId().ToString();

					string itemTypeName = configuration.DataMapping.GetTypeAttribute(SlamItemContext.ProcessedByContentType ? "" : parentList.ID.ToString(), typeName, "TableName");
					if (String.IsNullOrEmpty(itemTypeName))
					{
						itemTypeName = typeName;
					}

					if (SlamItemContext.ProcessedByContentType)
					{
						ContentItemServices.EnsureFrontEndUrlContentTypeField(listItem, listItem.ContentType);
						ContentItemServices.EnsureStatusContentTypeField(listItem, listItem.ContentType);
					}
					else
					{
						ContentItemServices.EnsureFrontEndUrlListField(listItem);
						ContentItemServices.EnsureStatusListField(listItem);
					}

					listItem.SetPublishingStatus();

					string siteCollection = listItem.Web.Site.ServerRelativeUrl.TrimStart('/');

					logger.LogMessage("Information", "ContentItem " + eventPrefix + " Start - " + logItemInformation);

					ContentItemProfileSection contentItemSection = configuration.GetSection<ContentItemProfileSection>("ContentItem");
					ContentItemConfiguredType contentItemType = contentItemSection.GetContentItemConfiguredType(siteCollection, SlamItemContext.ProcessedByContentType ? "" : parentList.ID.ToString(), typeName);

					List<Field> contentItemFields = contentItemSection.GetContentItemFields().ToList();
					IEnumerable<ContentItemField> typeContentItemFields = contentItemSection.GetTypeContentItemFields(siteCollection, SlamItemContext.ProcessedByContentType ? "" : parentList.ID.ToString(), SlamItemContext.TypeName);
					List<Field> outputFields = configuration.DataMapping.GetTypeFields(SlamItemContext.ProcessedByContentType ? "" : parentList.ID.ToString(), typeName).ToList();

					if (!contentItemDataService.VerifyContentItemTableExists(dataSchema))
					{
						if (!contentItemDataService.VerifyTableExists(dataSchema, "User"))
						{
							contentItemDataService.CreateUserTable(dataSchema);
						}

						logger.LogMessage("Information", "ContentItem " + eventPrefix + " - Create ContentItem Table - " + logItemInformation);
						contentItemDataService.CreateContentItemTable(dataSchema, contentItemFields);
					}

					Hashtable values = ContentItemServices.GetContentItemFieldValues(listItem, contentItemSection, contentItemType, contentItemFields, typeContentItemFields, outputFields, contentItemDataService, dataSchema);
					values.StripContainerDivFromValues();

					logger.LogMessage("Information", "ContentItem " + eventPrefix + " - Insert ContentItem - " + logItemInformation);

					contentItemDataService.InsertContentItem(dataSchema, values);

					site.AllowUnsafeUpdates = true;
					listItem["Front-End Url"] = listItem.FormatFrontEndUrl(contentItemType.FrontEndUrl, contentItemType);
					listItem.SystemUpdate(false);

					if (!contentItemType.ContentItemOnly)
					{
						logger.LogMessage("Information", eventPrefix + " Start - " + logItemInformation);
						
						for (int i = 0; i < outputFields.Count; i++)
						{
							if (typeContentItemFields.FirstOrDefault(f => f.Name == outputFields[i].Name) != null)
							{
								outputFields.RemoveAt(i);
								i--;
							}

							if (outputFields[i].Name == "ListID")
							{
								outputFields.RemoveAt(i);
								i--;
							}

						}

						Hashtable typeFieldValues = new Hashtable();
						try
						{
							if (!contentItemDataService.VerifyTableExists(dataSchema, GetTableName(itemTypeName)))
							{
								contentItemDataService.CreateContentTable(dataSchema, GetTableName(itemTypeName), outputFields);
							}
							else
							{
								contentItemDataService.UpdateContentTable(dataSchema, GetTableName(itemTypeName), outputFields);
							}

							typeFieldValues = listItem.GetFieldValues(outputFields);
							typeFieldValues.StripContainerDivFromValues();

							contentItemDataService.InsertItem(dataSchema, GetTableName(itemTypeName), typeFieldValues);
						}
						catch (SqlException)
						{
							logger.LogMessage("Information", eventPrefix + " Failed, re-slamming associated items and trying again - " + logItemInformation);
							listItem.EnsureSlamAssociations(outputFields, true);

							typeFieldValues = listItem.GetFieldValues(outputFields);
							typeFieldValues.StripContainerDivFromValues();
							contentItemDataService.InsertItem(dataSchema, GetTableName(itemTypeName), typeFieldValues);
						}

						logger.LogMessage("Information", eventPrefix + " Complete - " + logItemInformation);

						logger.LogMessage("Information", eventPrefix + " Associations - " + logItemInformation);

						IEnumerable<AssociationField> associationFields = configuration.DataMapping.GetTypeAssociations(SlamItemContext.ProcessedByContentType ? "" : parentList.ID.ToString(), typeName);
						listItem.EnsureSlamAssociations(associationFields.Cast<Field>().ToList(), false);

						//foreach (AssociationField association in associationFields)
						//{
						//    if (!String.IsNullOrEmpty(association.AssociationName) && association is SqlMappedAssociationField)
						//    {
						//        SqlMappedAssociationField sqlAssociationField = (SqlMappedAssociationField)association;
						//        foreach (SPListItem associatedItem in listItem.GetLookupValueListItems(association.Name))
						//        {
						//            logger.LogMessage("Information", eventPrefix + " Association - Type: " + association.AssociationName + " From: " + itemTypeName + " To: " + association.AssociatedTypeName);
						//            try
						//            {
						//                dataServices.Associate(association.AssociationName, sqlAssociationField.FirstAssociatedTypeIdColumn, listItem.GetUniqueId().ToString(), sqlAssociationField.SecondAssociatedTypeIdColumn, associatedItem.GetUniqueId().ToString());
						//            }
						//            catch
						//            {
						//                listItem.EnsureSlamAssociations(associationFields.Cast<Field>().ToList(), true);
						//                dataServices.Associate(association.AssociationName, sqlAssociationField.FirstAssociatedTypeIdColumn, listItem.GetUniqueId().ToString(), sqlAssociationField.SecondAssociatedTypeIdColumn, associatedItem.GetUniqueId().ToString());
						//            }
						//        }
						//    }
						//}
						logger.LogMessage("Information", eventPrefix + " Associations Complete - " + logItemInformation);

						string attachmentTable = configuration.DataMapping.GetTypeAttribute(SlamItemContext.ProcessedByContentType ? "" : parentList.ID.ToString(), typeName, "AttachmentTableName");

						if (!String.IsNullOrEmpty(attachmentTable))
						{
							logger.LogMessage("Information", eventPrefix + " Attachments - " + logItemInformation);

							Field idField = outputFields[0];
							string idColumnName = idField.Name;
							if (idField is SqlMappedField)
							{
								idColumnName = String.IsNullOrEmpty(((SqlMappedField)idField).Column) ? idColumnName : ((SqlMappedField)idField).Column;
							}

							string baseAttachmentUrl = listItem.Attachments.UrlPrefix.Replace(site.Url, "");
							foreach (string attachmentFileUrl in listItem.Attachments)
							{
								Hashtable attachmentValues = new Hashtable();
								attachmentValues.Add(idColumnName, typeFieldValues[idColumnName]);
								attachmentValues.Add("FileUrl", baseAttachmentUrl + attachmentFileUrl);
								dataServices.Insert(attachmentTable, attachmentValues, new object[] { true, GetTableName(itemTypeName) });
							}

							logger.LogMessage("Information", eventPrefix + " Attachments Complete - " + logItemInformation);
						}

						createReturn = true;
					}
					else
					{
						createReturn = true;
					}

					logger.LogMessage("Information", "ContentItem " + eventPrefix + " - Create Tag Associations - " + logItemInformation);

					//if (!contentItemDataService.VerifyContentItemTagTableExists(dataSchema))
					//{
					//    logger.LogMessage("Information", "ContentItem " + eventPrefix + " - Create ContentItem Tags Table - " + logItemInformation);
					//    SqlMappedAssociationField tagAssociationField = contentItemSection.GetContentItemTagAssociationField();
					//    contentItemDataService.CreateAssociationTable(dataSchema, tagAssociationField.AssociationName, tagAssociationField.FirstAssociatedTypeTableName, tagAssociationField.FirstAssociatedTypeIdColumn, tagAssociationField.FirstAssociatedTypeIdSqlType, tagAssociationField.FirstAssociatedTypeIdSourceColumn, tagAssociationField.SecondAssociatedTypeTableName, tagAssociationField.SecondAssociatedTypeIdColumn, tagAssociationField.SecondAssociatedTypeIdSqlType, tagAssociationField.SecondAssociatedTypeIdSourceColumn);
					//}

					if (!contentItemDataService.VerifyContentItemFeatureOnTagTableExists(dataSchema))
					{
						logger.LogMessage("Information", "ContentItem " + eventPrefix + " - Create Content Item Feature On Tag Table - " + logItemInformation);
						SqlMappedAssociationField tagAssociationField = contentItemSection.GetContentItemFeatureOnTagAssociationField();
						contentItemDataService.CreateContentItemFeatureOnTagTable(dataSchema, tagAssociationField.AssociationName, tagAssociationField.FirstAssociatedTypeTableName, tagAssociationField.FirstAssociatedTypeIdColumn, tagAssociationField.FirstAssociatedTypeIdSqlType, tagAssociationField.FirstAssociatedTypeIdSourceColumn, tagAssociationField.SecondAssociatedTypeTableName, tagAssociationField.SecondAssociatedTypeIdColumn, tagAssociationField.SecondAssociatedTypeIdSqlType, tagAssociationField.SecondAssociatedTypeIdSourceColumn);
					}

					if (!contentItemDataService.VerifyContentItemFeatureLocationTableExists(dataSchema))
					{
						logger.LogMessage("Information", "ContentItem " + eventPrefix + " - Create Content Item Feature Location Table - " + logItemInformation);
						SqlMappedAssociationField featureLocationAssociationField = contentItemSection.GetContentItemFeatureLocationAssociationField();
						contentItemDataService.CreateContentItemFeatureLocationTable(dataSchema, featureLocationAssociationField.AssociationName, featureLocationAssociationField.FirstAssociatedTypeTableName, featureLocationAssociationField.FirstAssociatedTypeIdColumn, featureLocationAssociationField.FirstAssociatedTypeIdSqlType, featureLocationAssociationField.FirstAssociatedTypeIdSourceColumn, featureLocationAssociationField.SecondAssociatedTypeTableName, featureLocationAssociationField.SecondAssociatedTypeIdColumn, featureLocationAssociationField.SecondAssociatedTypeIdSqlType, featureLocationAssociationField.SecondAssociatedTypeIdSourceColumn);
					}

					List<string> associatedTagIds = new List<string>();
					List<string> associatedFeatureTagIds = new List<string>();
					foreach (SPField field in listItem.Fields)
					{
						if (field is SlamField && listItem.GetUniqueId() != null)
						{
							SlamField SlamField = (SlamField)field;

							//if (listItem[SlamField.Id] != null && SlamField.UserControl.Contains("TagFieldControl"))
							//{
							//    foreach (SPListItem associatedItem in listItem.GetLookupValueListItems(tagField.InternalName))
							//    {
							//        string associatedId = Convert.ToString(associatedItem.GetUniqueId());
							//        if (!associatedTagIds.Contains(associatedId))
							//        {
							//            logger.LogMessage("Information", "ContentItem " + eventPrefix + " = Create Content Item Tag Association - From: " + listItem.GetUniqueId().ToString() + " To: " + associatedId);
							//            try
							//            {
							//                contentItemDataService.InsertContentItemTagItem(dataSchema, listItem.GetUniqueId().ToString(), associatedId);
							//                associatedTagIds.Add(associatedId);
							//            }
							//            catch
							//            {
							//                associatedItem.SlamUpdate();
							//                contentItemDataService.InsertContentItemTagItem(dataSchema, listItem.GetUniqueId().ToString(), associatedId);
							//                associatedTagIds.Add(associatedId);
							//            }
							//        }
							//    }
							//}

							if (listItem[SlamField.Id] != null && SlamField.UserControl.Contains("FeatureOnTagField"))
							{
								List<FeatureOnTagValue> featureOnValues = FeatureOnTagFieldServices.ParseFeatureOnTagValue((string)listItem[SlamField.Title]);

								foreach (FeatureOnTagValue featureValue in featureOnValues)
								{
									if (featureValue.Type == FeatureType.Tag)
									{
										if (!associatedFeatureTagIds.Contains(featureValue.Id))
										{
											logger.LogMessage("Information", "ContentItem " + eventPrefix + " - Content Item Feature On Tag Association - From: " + listItem.GetUniqueId().ToString() + " To: " + featureValue.Id);
											contentItemDataService.InsertContentItemFeatureOnTagItem(dataSchema, listItem.GetUniqueId().ToString(), featureValue.Id, Int32.Parse(featureValue.FeatureLevel));
											associatedFeatureTagIds.Add(featureValue.Id);
										}
									}
									else
									{
										logger.LogMessage("Information", "ContentItem " + eventPrefix + " - Content Item Feature Feature Location Association - From: " + listItem.GetUniqueId().ToString() + " To: " + featureValue.Id);
										contentItemDataService.InsertContentItemFeatureLocationItem(dataSchema, listItem.GetUniqueId().ToString(), featureValue.Id, Int32.Parse(featureValue.FeatureLevel));
									}
								}
							}
						}
					}

					string id = Convert.ToString(listItem.GetUniqueId());
					logger.LogMessage("Information", eventPrefix + " Calling Clear Cache URLs - " + logItemInformation);
					foreach (ClearCacheUrl clearCacheUrl in contentItemSection.GetClearCacheUrls())
					{
						ContentItemServices.ExecuteClearCacheUrl(clearCacheUrl.Url.TrimEnd('/') + "/" + id, clearCacheUrl.Uri, clearCacheUrl.AuthType, clearCacheUrl.UserName, clearCacheUrl.Password, clearCacheUrl.Domain);
					}

					try
					{
						if (SlamCmsContext.Data != null)
							SlamCmsContext.Data.Cache.InvalidateQueries();
					}
					catch (Exception ex) //If cache invalidation fails, it should stop this process
					{
						logger.LogMessage("Exception", ex.ToString());
					}

					logger.LogMessage("Information", "ContentItem " + eventPrefix + " Complete - " + logItemInformation);
				}
				catch (Exception ex)
				{
					logger.LogMessage("Exception", ex.ToString());
					createReturn = false;
				}

				logger.LogEvent("Create", SlamItemContext.TypeName, "List: " + parentList.Title + Environment.NewLine + "ListItem: " + listItem.GetTitle() + ", " + listItem.GetUniqueId().ToString());
			}

			return createReturn;
		}

		internal static bool Delete(SPListItem listItem, IDataServices dataServices, IConfigurationManager configuration, ILogger logger, DataService contentItemDataService, string dataSchema, string eventPrefix)
		{
			bool deleteReturn = false;
			SPWeb site = listItem.Web;
			SPList list = listItem.ParentList;

			try
			{
				string typeName = SlamItemContext.TypeName;
				string logItemInformation = typeName + Environment.NewLine + " List: " + list.Title + Environment.NewLine + "ListItem: " + listItem.GetTitle() + ", " + listItem.GetUniqueId().ToString();


				string itemTypeName = configuration.DataMapping.GetTypeAttribute(SlamItemContext.ProcessedByContentType ? "" : list.ID.ToString(), typeName, "TableName");
				if (String.IsNullOrEmpty(itemTypeName))
				{
					itemTypeName = typeName;
				}

				listItem.SetPublishingStatus();

				string id = Convert.ToString(listItem.GetUniqueId());

				string siteCollection = listItem.Web.Site.ServerRelativeUrl.TrimStart('/');

				logger.LogMessage("Information", "ContentItem " + eventPrefix + " Start - " + logItemInformation);

				ContentItemProfileSection contentItemSection = configuration.GetSection<ContentItemProfileSection>("ContentItem");
				ContentItemConfiguredType contentItemType = contentItemSection.GetContentItemConfiguredType(siteCollection, SlamItemContext.ProcessedByContentType ? "" : list.ID.ToString(), typeName);

				if (!contentItemType.ContentItemOnly)
				{
					logger.LogMessage("Information", eventPrefix + " Associations - " + logItemInformation);

					List<string> clearedAssociations = new List<string>();

					foreach (AssociationField association in configuration.DataMapping.GetTypeAssociations(SlamItemContext.ProcessedByContentType ? "" : list.ID.ToString(), typeName))
					{
						if (!String.IsNullOrEmpty(association.AssociationName) && association is SqlMappedAssociationField && listItem.Fields.Cast<SPField>().Where(f => f.Title.Equals(association.Name, StringComparison.CurrentCultureIgnoreCase) || f.InternalName.Equals(association.Name, StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault() != null)
						{
							SqlMappedAssociationField sqlAssociationField = (SqlMappedAssociationField)association;

							if (!clearedAssociations.Contains(association.AssociationName))
							{
								logger.LogMessage("Information", eventPrefix + " Associations - For: " + itemTypeName + " Type: " + association.AssociationName);
								dataServices.DeleteAllAssociations(association.AssociationName, sqlAssociationField.FirstAssociatedTypeIdColumn, id);
								clearedAssociations.Add(association.AssociationName);
							}
						}
					}
					logger.LogMessage("Information", eventPrefix + " Associations Complete - " + logItemInformation);

					string attachmentTable = configuration.DataMapping.GetTypeAttribute(SlamItemContext.ProcessedByContentType ? "" : list.ID.ToString(), typeName, "AttachmentTableName");

					if (!String.IsNullOrEmpty(attachmentTable))
					{
						logger.LogMessage("Information", eventPrefix + " Attachments - " + logItemInformation);

						List<Field> outputFields = configuration.DataMapping.GetTypeFields(SlamItemContext.ProcessedByContentType ? "" : list.ID.ToString(), typeName).ToList();
						Field idField = outputFields[0];
						string idColumnName = idField.Name;
						if (idField is SqlMappedField)
						{
							idColumnName = String.IsNullOrEmpty(((SqlMappedField)idField).Column) ? idColumnName : ((SqlMappedField)idField).Column;
						}

						dataServices.Delete(attachmentTable, Convert.ToString(listItem.GetUniqueId()), new object[] { true, idColumnName });

						logger.LogMessage("Information", eventPrefix + " Attachments Complete - " + logItemInformation);
					}

					logger.LogMessage("Information", eventPrefix + " Start - " + logItemInformation);

					dataServices.Delete(itemTypeName, id);

					logger.LogMessage("Information", eventPrefix + " Complete - " + logItemInformation);

					deleteReturn = true;
				}
				else
				{
					deleteReturn = true;
				}

				logger.LogMessage("Information", "ContentItem " + eventPrefix + " - Delete Tag Associations - " + logItemInformation);

				if (contentItemDataService.VerifyContentItemTagTableExists(dataSchema) && !String.IsNullOrEmpty(id))
				{
					logger.LogMessage("Information", "ContentItem " + eventPrefix + " - Delete ContentItem Tags for Item - " + id);
					contentItemDataService.DeleteContentItems(dataSchema, "ContentItemTag", id);
				}

				if (contentItemDataService.VerifyContentItemFeatureLocationTableExists(dataSchema) && !String.IsNullOrEmpty(id))
				{
					logger.LogMessage("Information", "ContentItem " + eventPrefix + " - Delete ContentItem Feature Locations for Item - " + id);
					contentItemDataService.DeleteContentItems(dataSchema, "ContentItemFeatureLocation", id);
				}

				if (contentItemDataService.VerifyContentItemFeatureOnTagTableExists(dataSchema) && !String.IsNullOrEmpty(id))
				{
					logger.LogMessage("Information", "ContentItem " + eventPrefix + " - Delete ContentItem Feature On Tags for Item - " + id);
					contentItemDataService.DeleteContentItems(dataSchema, "ContentItemFeatureOnTag", id);
				}

				logger.LogMessage("Information", "ContentItem " + eventPrefix + " - Delete Start - " + logItemInformation);

				if (!contentItemDataService.VerifyContentItemTableExists(dataSchema))
				{
					if (!contentItemDataService.VerifyTableExists(dataSchema, "User"))
					{
						contentItemDataService.CreateUserTable(dataSchema);
					}

					logger.LogMessage("Information", "ContentItem " + eventPrefix + " - Create ContentItem Table - " + logItemInformation);
					contentItemDataService.CreateContentItemTable(dataSchema, contentItemSection.GetContentItemFields());
				}
				else
				{
					logger.LogMessage("Information", "ContentItem " + eventPrefix + " - Delete ContentItem - " + logItemInformation);
					contentItemDataService.DeleteContentItems(dataSchema, "ContentItem", id);
				}

				foreach (ClearCacheUrl clearCacheUrl in contentItemSection.GetClearCacheUrls())
				{
					ContentItemServices.ExecuteClearCacheUrl(clearCacheUrl.Url.TrimEnd('/') + "/" + id, clearCacheUrl.Uri, clearCacheUrl.AuthType, clearCacheUrl.UserName, clearCacheUrl.Password, clearCacheUrl.Domain);
				}

				try
				{
					if (SlamCmsContext.Data != null)
						SlamCmsContext.Data.Cache.InvalidateQueries();
				}
				catch (Exception ex) //If cache invalidation fails, it should stop this process
				{
					logger.LogMessage("Exception", ex.ToString());
				}
			}
			catch (Exception ex)
			{
				logger.LogMessage("Exception", ex.ToString());
				deleteReturn = false;
			}

			logger.LogEvent("Delete", SlamItemContext.TypeName, "List: " + list.Title + Environment.NewLine + "ListItem: " + listItem.GetTitle() + ", " + listItem.GetUniqueId().ToString());

			return deleteReturn;
		}

		internal static bool Update(SPListItem listItem, IDataServices dataServices, IConfigurationManager configuration, ILogger logger, DataService contentItemDataService, string dataSchema, string eventPrefix, bool commitItemChanges)
		{
			bool updateReturn = false;
			SPWeb site = listItem.Web;
			SPList list = listItem.ParentList;

			try
			{
				string typeName = SlamItemContext.TypeName;
				string logItemInformation = typeName + Environment.NewLine + " List: " + list.Title + Environment.NewLine + "ListItem: " + listItem.GetTitle() + ", " + listItem.GetUniqueId().ToString();
				logger.LogMessage("Information", eventPrefix + " Start - " + logItemInformation);

				string itemTypeName = configuration.DataMapping.GetTypeAttribute(SlamItemContext.ProcessedByContentType ? "" : list.ID.ToString(), typeName, "TableName");
				if (String.IsNullOrEmpty(itemTypeName))
				{
					itemTypeName = typeName;
				}
				
				string status = listItem.GetPublishingStatus();
				string id = Convert.ToString(listItem.GetUniqueId());

				if (commitItemChanges)
				{
					//Modify ID for preview or live versions
					if (list.EnableModeration && list.EnableVersioning && listItem.HasPublishedVersion && status != ContentItemStatus.Live.Name)
					{
						id = Regex.Replace(id, "-p", "") + "-p";

						listItem["SLAM ID"] = id;
						listItem.Web.AllowUnsafeUpdates = true;
						listItem.SystemUpdate(false);
					}
					else if (list.EnableModeration && list.EnableVersioning && status == ContentItemStatus.Live.Name)
					{
						id = Regex.Replace(id, "-p", "");

						listItem["SLAM ID"] = id;
						listItem.Web.AllowUnsafeUpdates = true;
						listItem.SystemUpdate(false);
					}
				}

				if (commitItemChanges)
				{
					//Set Live Version ID
					if (list.EnableVersioning && status == ContentItemStatus.Live.Name)
					{
						listItem.SetLiveVersionId();
					}
				}

				string siteCollection = listItem.Web.Site.ServerRelativeUrl.TrimStart('/');

				logger.LogMessage("Information", "ContentItem " + eventPrefix + " Start - " + logItemInformation);

				ContentItemProfileSection contentItemSection = configuration.GetSection<ContentItemProfileSection>("ContentItem");
				ContentItemConfiguredType contentItemType = contentItemSection.GetContentItemConfiguredType(siteCollection, SlamItemContext.ProcessedByContentType ? "" : list.ID.ToString(), typeName);

				List<Field> contentItemFields = contentItemSection.GetContentItemFields().ToList();
				IEnumerable<ContentItemField> typeContentItemFields = contentItemSection.GetTypeContentItemFields(siteCollection, SlamItemContext.ProcessedByContentType ? "" : list.ID.ToString(), SlamItemContext.TypeName);
				List<Field> outputFields = configuration.DataMapping.GetTypeFields(SlamItemContext.ProcessedByContentType ? "" : list.ID.ToString(), typeName).ToList();

				if (!contentItemDataService.VerifyContentItemTableExists(dataSchema))
				{
					if (!contentItemDataService.VerifyTableExists(dataSchema, "User"))
					{
						contentItemDataService.CreateUserTable(dataSchema);
					}

					logger.LogMessage("Information", "ContentItem " + eventPrefix + " - Create ContentItem Table - " + logItemInformation);
					contentItemDataService.CreateContentItemTable(dataSchema, contentItemFields);
				}

				if (commitItemChanges)
				{
					if (SlamItemContext.ProcessedByContentType)
					{	
						ContentItemServices.EnsureFrontEndUrlContentTypeField(listItem, listItem.ContentType);
						ContentItemServices.EnsureStatusContentTypeField(listItem, listItem.ContentType);
					}
					else
					{	
						ContentItemServices.EnsureFrontEndUrlListField(listItem);
						ContentItemServices.EnsureStatusListField(listItem);
					}
				}

				Hashtable values = ContentItemServices.GetContentItemFieldValues(listItem, contentItemSection, contentItemType, contentItemFields, typeContentItemFields, outputFields, contentItemDataService, dataSchema);
				values.StripContainerDivFromValues();

				logger.LogMessage("Information", "ContentItem CT Update - Update ContentItem - " + logItemInformation);
				if (!contentItemDataService.UpdateContentItem(dataSchema, id, values))
				{
					logger.LogMessage("Information", "ContentItem CT Update - Insert ContentItem - " + logItemInformation);

					contentItemDataService.InsertContentItem(dataSchema, values);
				}

				if (commitItemChanges)
				{
					if (listItem["Front-End Url"] == null || (listItem["Front-End Url"].ToString() != listItem.FormatFrontEndUrl(contentItemType.FrontEndUrl, contentItemType)))
					{
						site.AllowUnsafeUpdates = true;
						listItem["Front-End Url"] = listItem.FormatFrontEndUrl(contentItemType.FrontEndUrl, contentItemType);
						listItem.SystemUpdate(false);
					}
				}

				if (!contentItemType.ContentItemOnly)
				{
					logger.LogMessage("Information", eventPrefix + " Start - " + logItemInformation);
					
					string idColumn = outputFields[0] is SqlMappedField ? ((SqlMappedField)outputFields[0]).Column : outputFields[0].Name;
					for (int i = 0; i < outputFields.Count; i++)
					{
						if (typeContentItemFields.FirstOrDefault(f => f.Name == outputFields[i].Name) != null)
						{
							outputFields.RemoveAt(i);
							i--;
						}

						if (outputFields[i].Name == "ListID")
						{
							outputFields.RemoveAt(i);
							i--;
						}
					}

					Hashtable typeFieldValues = new Hashtable();
					try
					{
						if (!contentItemDataService.VerifyTableExists(dataSchema, GetTableName(itemTypeName)))
						{
							contentItemDataService.CreateContentTable(dataSchema, GetTableName(itemTypeName), outputFields);
						}
						else
						{
							contentItemDataService.UpdateContentTable(dataSchema, GetTableName(itemTypeName), outputFields);
						}

						typeFieldValues = listItem.GetFieldValues(outputFields);
						typeFieldValues.StripContainerDivFromValues();

						if (!contentItemDataService.UpdateItem(dataSchema, GetTableName(itemTypeName), id, typeFieldValues))
						{	
							contentItemDataService.InsertItem(dataSchema, GetTableName(itemTypeName), typeFieldValues);
						}
					}
					catch (SqlException)
					{
						logger.LogMessage("Information", eventPrefix + " Failed, re-slamming associated items and trying again - " + logItemInformation);
						listItem.EnsureSlamAssociations(outputFields, true);

						typeFieldValues = listItem.GetFieldValues(outputFields);
						typeFieldValues.StripContainerDivFromValues();

						if (!contentItemDataService.UpdateItem(dataSchema, GetTableName(itemTypeName), id, typeFieldValues))
						{
							contentItemDataService.InsertItem(dataSchema, GetTableName(itemTypeName), typeFieldValues);
						}
					}

					logger.LogMessage("Information", eventPrefix + " Complete - " + logItemInformation);

					List<string> clearedAssociations = new List<string>();

					logger.LogMessage("Information", eventPrefix + " Associations - " + logItemInformation);

					IEnumerable<AssociationField> associationFields = configuration.DataMapping.GetTypeAssociations(SlamItemContext.ProcessedByContentType ? "" : list.ID.ToString(), typeName);
					listItem.EnsureSlamAssociations(associationFields.Cast<Field>().ToList(), false);

					foreach (AssociationField association in associationFields)
					{
						if (!String.IsNullOrEmpty(association.AssociationName) && association is SqlMappedAssociationField)
						{
							SqlMappedAssociationField sqlAssociationField = (SqlMappedAssociationField)association;
							if (!clearedAssociations.Contains(association.AssociationName))
							{
								logger.LogMessage("Information", eventPrefix + " Delete Associations - For: " + itemTypeName + " Type: " + association.AssociationName);
								dataServices.DeleteAllAssociations(association.AssociationName, sqlAssociationField.FirstAssociatedTypeIdColumn, id);
								clearedAssociations.Add(association.AssociationName);
							}
							foreach (SPListItem associatedItem in listItem.GetLookupValueListItems(association.Name))
							{
								if (associatedItem.VerifyItemRequiresSlamUpdate())
								{
									associatedItem.SlamUpdate();
								}
								logger.LogMessage("Information", eventPrefix + " Association - Type: " + association.AssociationName + " From: " + itemTypeName + " To: " + association.AssociatedTypeName);
								try
								{
									dataServices.Associate(association.AssociationName, sqlAssociationField.FirstAssociatedTypeIdColumn, id, sqlAssociationField.SecondAssociatedTypeIdColumn, associatedItem.GetUniqueId().ToString());
								}
								catch
								{
									logger.LogMessage("Information", eventPrefix + " Association failed, re-slamming associated items and trying again - Type: " + association.AssociationName + " From: " + itemTypeName + " To: " + association.AssociatedTypeName);
									listItem.EnsureSlamAssociations(associationFields.Cast<Field>().ToList(), true);
									dataServices.Associate(association.AssociationName, sqlAssociationField.FirstAssociatedTypeIdColumn, id, sqlAssociationField.SecondAssociatedTypeIdColumn, associatedItem.GetUniqueId().ToString());
								}
							}
						}
					}
					logger.LogMessage("Information", eventPrefix + " Associations Complete - " + logItemInformation);

					string attachmentTable = configuration.DataMapping.GetTypeAttribute(SlamItemContext.ProcessedByContentType ? "" : list.ID.ToString(), typeName, "AttachmentTableName");

					if (!String.IsNullOrEmpty(attachmentTable))
					{
						logger.LogMessage("Information", eventPrefix + " Attachments - " + logItemInformation);

						Field idField = outputFields[0];
						string idColumnName = idField.Name;
						if (idField is SqlMappedField)
						{
							idColumnName = String.IsNullOrEmpty(((SqlMappedField)idField).Column) ? idColumnName : ((SqlMappedField)idField).Column;
						}

						logger.LogMessage("Information", eventPrefix + " Delete Attachments - " + logItemInformation);
						dataServices.Delete(attachmentTable, Convert.ToString(typeFieldValues[idColumnName]), new object[] { true, idColumnName });

						string baseAttachmentUrl = listItem.Attachments.UrlPrefix.Replace(site.Url, "");
						foreach (string attachmentFileUrl in listItem.Attachments)
						{
							Hashtable attachmentValues = new Hashtable();
							attachmentValues.Add(idColumnName, typeFieldValues[idColumnName]);
							attachmentValues.Add("FileUrl", baseAttachmentUrl + attachmentFileUrl);
							dataServices.Insert(attachmentTable, attachmentValues, new object[] { true, GetTableName(itemTypeName) });
						}

						logger.LogMessage("Information", eventPrefix + " Attachments Complete - " + logItemInformation);
					}
					updateReturn = true;
				}
				else
				{
					updateReturn = true;
				}

				logger.LogMessage("Information", "ContentItem CT Update - Update Tag Associations - " + logItemInformation);

				//if (!contentItemDataService.VerifyContentItemTagTableExists(dataSchema))
				//{
				//    logger.LogMessage("Information", "ContentItem " + eventPrefix + " - Create ContentItem Tags Table - " + logItemInformation);
				//    SqlMappedAssociationField tagAssociationField = contentItemSection.GetContentItemTagAssociationField();
				//    contentItemDataService.CreateAssociationTable(dataSchema, tagAssociationField.AssociationName, tagAssociationField.FirstAssociatedTypeTableName, tagAssociationField.FirstAssociatedTypeIdColumn, tagAssociationField.FirstAssociatedTypeIdSqlType, tagAssociationField.FirstAssociatedTypeIdSourceColumn, tagAssociationField.SecondAssociatedTypeTableName, tagAssociationField.SecondAssociatedTypeIdColumn, tagAssociationField.SecondAssociatedTypeIdSqlType, tagAssociationField.SecondAssociatedTypeIdSourceColumn);
				//}
				//else
				//{
				//    logger.LogMessage("Information", "ContentItem " + eventPrefix + " - Delete ContentItem Tags for Item - " + id);
				//    contentItemDataService.DeleteContentItems(dataSchema, "ContentItemTag", id);
				//}

				if (!contentItemDataService.VerifyContentItemFeatureOnTagTableExists(dataSchema))
				{
					logger.LogMessage("Information", "ContentItem " + eventPrefix + " - Create Content Item Feature On Tag Table - " + logItemInformation);
					SqlMappedAssociationField tagAssociationField = contentItemSection.GetContentItemFeatureOnTagAssociationField();
					contentItemDataService.CreateContentItemFeatureOnTagTable(dataSchema, tagAssociationField.AssociationName, tagAssociationField.FirstAssociatedTypeTableName, tagAssociationField.FirstAssociatedTypeIdColumn, tagAssociationField.FirstAssociatedTypeIdSqlType, tagAssociationField.FirstAssociatedTypeIdSourceColumn, tagAssociationField.SecondAssociatedTypeTableName, tagAssociationField.SecondAssociatedTypeIdColumn, tagAssociationField.SecondAssociatedTypeIdSqlType, tagAssociationField.SecondAssociatedTypeIdSourceColumn);
				}
				else
				{
					logger.LogMessage("Information", "ContentItem " + eventPrefix + " - Delete ContentItem Feature On Tag for Item - " + id);
					contentItemDataService.DeleteContentItems(dataSchema, "ContentItemFeatureOnTag", id);
				}

				if (!contentItemDataService.VerifyContentItemFeatureLocationTableExists(dataSchema))
				{
					logger.LogMessage("Information", "ContentItem " + eventPrefix + " - Create Content Item Feature Location Table - " + logItemInformation);
					SqlMappedAssociationField featureLocationAssociationField = contentItemSection.GetContentItemFeatureLocationAssociationField();
					contentItemDataService.CreateContentItemFeatureLocationTable(dataSchema, featureLocationAssociationField.AssociationName, featureLocationAssociationField.FirstAssociatedTypeTableName, featureLocationAssociationField.FirstAssociatedTypeIdColumn, featureLocationAssociationField.FirstAssociatedTypeIdSqlType, featureLocationAssociationField.FirstAssociatedTypeIdSourceColumn, featureLocationAssociationField.SecondAssociatedTypeTableName, featureLocationAssociationField.SecondAssociatedTypeIdColumn, featureLocationAssociationField.SecondAssociatedTypeIdSqlType, featureLocationAssociationField.SecondAssociatedTypeIdSourceColumn);
				}
				else
				{
					logger.LogMessage("Information", "ContentItem " + eventPrefix + " - Delete ContentItem Feature Location for Item - " + id);
					contentItemDataService.DeleteContentItems(dataSchema, "ContentItemFeatureLocation", id);
				}

				List<string> associatedTagIds = new List<string>();
				List<string> associatedFeatureTagIds = new List<string>();
				foreach (SPField field in listItem.Fields)
				{
					if (field is SlamField && listItem.GetUniqueId() != null)
					{
						SlamField SlamField = (SlamField)field;

						//if (listItem[SlamField.Id] != null && SlamField.UserControl.Contains("TagField"))
						//{
						//    foreach (SPListItem associatedItem in listItem.GetLookupValueListItems(SlamField.InternalName))
						//    {
						//        string associatedId = Convert.ToString(associatedItem.GetUniqueId());
						//        if (!associatedTagIds.Contains(associatedId))
						//        {
						//            logger.LogMessage("Information", "ContentItem " + eventPrefix + " = Create Content Item Tag Association - From: " + id + " To: " + associatedId);

						//            try
						//            {
						//                contentItemDataService.InsertContentItemTagItem(dataSchema, id, associatedId);
						//                associatedTagIds.Add(associatedId);
						//            }
						//            catch
						//            {
						//                associatedItem.SlamUpdate();
						//                contentItemDataService.InsertContentItemTagItem(dataSchema, id, associatedId);
						//                associatedTagIds.Add(associatedId);
						//            }
						//        }
						//    }
						//}



						if (listItem[SlamField.Id] != null && SlamField.UserControl.Contains("FeatureOnTagField"))
						{
							List<FeatureOnTagValue> featureOnValues = FeatureOnTagFieldServices.ParseFeatureOnTagValue((string)listItem[SlamField.Title]);

							foreach (FeatureOnTagValue featureValue in featureOnValues)
							{
								if (featureValue.Type == FeatureType.Tag)
								{
									if (!associatedFeatureTagIds.Contains(featureValue.Id))
									{
										logger.LogMessage("Information", "ContentItem " + eventPrefix + " - Content Item Feature On Tag Association - From: " + id + " To: " + featureValue.Id);
										contentItemDataService.InsertContentItemFeatureOnTagItem(dataSchema, id, featureValue.Id, Int32.Parse(featureValue.FeatureLevel));
										associatedFeatureTagIds.Add(featureValue.Id);
									}
								}
								else
								{
									logger.LogMessage("Information", "ContentItem " + eventPrefix + " - Content Item Feature Location Association - From: " + id + " To: " + featureValue.Id);
									contentItemDataService.InsertContentItemFeatureLocationItem(dataSchema, id, featureValue.Id, Int32.Parse(featureValue.FeatureLevel));
								}
							}
						}
					}
				}

				logger.LogMessage("Information", eventPrefix + " Calling Clear Cache URLs - " + logItemInformation);
				foreach (ClearCacheUrl clearCacheUrl in contentItemSection.GetClearCacheUrls())
				{
					ContentItemServices.ExecuteClearCacheUrl(clearCacheUrl.Url.TrimEnd('/') + "/" + id, clearCacheUrl.Uri, clearCacheUrl.AuthType, clearCacheUrl.UserName, clearCacheUrl.Password, clearCacheUrl.Domain);
				}
				
				try
				{
					if (SlamCmsContext.Data != null)
						SlamCmsContext.Data.Cache.InvalidateQueries();
				}
				catch (Exception ex) //If cache invalidation fails, it should stop this process
				{
					logger.LogMessage("Exception", ex.ToString());
				}

				logger.LogMessage("Information", "ContentItem " + eventPrefix + " Complete - " + logItemInformation);

				if (status != ContentItemStatus.Live.Name && listItem.HasPublishedVersion && !contentItemDataService.VerifyContentItemExists(dataSchema, values["Primary" + (contentItemFields[0] is SqlMappedField ? ((SqlMappedField)contentItemFields[0]).Column : contentItemFields[0].Name)].ToString()))
				{
					SPListItem liveListItem = ContentItemServices.GetDummyLiveListItem(listItem);
					SlamItemContext.ListItem = liveListItem;
					ContentItemEvents.Update(liveListItem, dataServices, configuration, logger, contentItemDataService, dataSchema, eventPrefix, false);
					SlamItemContext.ListItem = listItem;
				}
			}
			catch (Exception ex)
			{
				logger.LogMessage("Exception", ex.ToString());
				updateReturn = false;
			}

			logger.LogEvent("Update", SlamItemContext.TypeName, "List: " + list.Title + Environment.NewLine + "ListItem: " + listItem.GetTitle() + ", " + listItem.GetUniqueId().ToString());

			return updateReturn;
		}

		private static string GetTableName(string typeName)
		{
			return Regex.Replace(typeName, "[\\W\\s]", "");
		}
	}
}
